<template>
    <!-- class: 设置页面高度占满、启用 flex 布局、设置子元素垂直排列、当内容超出容器高度时，自动显示垂直滚动条； -->
    <div class="flex-1 flex flex-col backdrop-blur-sm bg-white/80 w-full rounded-2xl px-3 py-5 overflow-hidden"
        ref="chatContainer">

        <!-- 当前聊天 AI 的基本信息 -->
        <div v-if="roleDetailLoading">
            <div class="mx-8 my-2">
                <Skeleton class="h-4 w-24 mb-2"/>
                <Skeleton class="h-3 w-32"/>
            </div>
        </div>
        <div v-else>
            <div class="flex flex-col mx-8 gap-1 shrink-0">
                <div class="flex items-end gap-2">
                    <span>{{ role?.nickname }}</span>
                    <span v-show="roleChatting" class="text-xs">正在输入中......</span>
                </div>
                <span class="text-xs text-[#777777]">内容由 AI 生成</span>
            </div>
        </div>

        <!-- 分割线 -->
        <div class="border-t border-[#D5D5D5] mx-6 my-2 shrink-0"></div>

        <!-- 聊天记录区域 -->
        <!-- class: 让元素在 Flex 容器中自动填充剩余可用空间、最大宽度为 3xl、水平居中；设置下、上、x 轴水平方向的内边距 元素文本从右向左排列 -->
        <div class="flex-1 flex flex-col rtl w-full px-4 pb-4 pt-4 custom-scrollbar overflow-y-auto"
            ref="chatHistoryContainer"
            >

            <!-- 加载动画 -->
            <div v-show="isFirstLoad"
                class="flex-1 flex items-center justify-center z-10 transition-opacity duration-300 ease-out">
                <SvgIcon name="loading-twotone-loop" class="w-12 h-12 text-[1.1rem] text-gray-400"></SvgIcon>
            </div>

            <!-- 将聊天记录逆序排列 从而达到向上加载聊天记录流畅的效果 -->
            <div :class="!isFirstLoad ? 'visible opacity-100' : 'invisible opacity-0'"
                class="rotate-180 rtl transition-opacity duration-300 ease-in-out">
                <!-- 遍历聊天记录 -->
                <template v-for="(chat, index) in chatList" :key="index">
                    <!-- 用户提问消息 (靠右) -->
                    <!-- class: 启用 Flex 布局，子元素在主轴（水平方向）上向右对齐，并设置下边距；父容器进行了反转，因此子容器也反转，那就是，没有反转 -->
                    <div v-if="chat.role === 'user'" class="flex justify-end mb-4 rotate-text">
                        
                        <div class="quesiton-container">
                            <p>{{ chat.content }}</p>
                        </div>

                        <img src="http://t4hiq2jzc.hn-bkt.clouddn.com/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20241031141912.jpg"
                        class="w-9 h-9 rounded-full ml-3 shrink-0">
                    </div>

                    <!-- 大模型回复消息 (靠左) -->
                    <div v-else class="flex mb-4 rotate-text">
                        <!-- 头像 -->
                        <!-- class: 禁止 Flex 子元素收缩（防止被压缩，保持原始尺寸）, 并设置右边距； -->
                        <div class="shrink-0 mr-3">

                            <Skeleton v-if="roleDetailLoading" :rounded="false" class="w-9 h-9 rounded-full"/>
                            <!-- AI 头像 -->
                            <img v-else :src="role?.avatar" class="w-9 h-9 rounded-full items-center">

                        </div>
                        <!-- 回复的内容 -->
                        <!-- class: 设置内边距、最大宽度为父容器宽度的 90%，以及尽可能占满最大宽度； -->
                        <div class="flex flex-col max-w-[90%] 
                            bg-white rounded-2xl py-2 px-4">
                            <StreamMarkdownRender :content="chat.content" />

                            <!-- AI 对话附加功能 -->
                            <div class="flex justify-between mt-2">
                                <AudioWave
                                    :url="chat.audioUrl"
                                    color="#9AC2FF"
                                    :generateUrlFn="() => generateRoleAudio(chat)">
                                </AudioWave>
                            </div>
                        </div>
                    </div>
                </template>
            </div>
        </div>

        <ChatInputBox
            v-model="message"
            placeholder="一起聊天吧！"
            :textModels="role?.textModels"
            :soundModels="role?.soundModels"
            :minRows="2"
            :maxRows="6"
            containerClass="mx-auto bottom-8 w-full shrink-0"
            smartTextareaClass="bg-transparent border-none outline-none w-full text-sm pr-4 pl-6 resize-none overflow-y-auto"
            @sendMessage="sendMessage"
            :skeletonLoadingAnimation="chatInputBoxskeletonLoadingAnimation"
        />  
    </div>
</template>

<script setup>
import { ref, onBeforeUnmount, nextTick, onMounted, watch, computed } from 'vue';
import { useRoute } from 'vue-router'
import StreamMarkdownRender from '@/components/common/StreamMarkdownRender.vue'
import ChatInputBox from '@/components/ai/ChatInputBox.vue';
import { findChatMessagePageList } from '@/api/chat'
import Skeleton from '@/components/common/Skeleton.vue'
import AudioWave from '@/components/ai/AudioWave.vue'
import { useRoleChatStore } from '@/stores/roleChatStore'
import { fetchEventSource } from '@microsoft/fetch-event-source';
import SvgIcon from '@/components/common/SvgIcon.vue';
import { findRoleById, roleSoundGgeneration } from '@/api/role';

const route = useRoute()

// 对话 ID
const chatId = ref(route.params.chatId || null)

// 输入的消息
const message = ref(history.state?.firstMessage || '')

// 聊天记录 (给个默认的问候语)
const chatList = ref([])

// 获取 chat store
const roleChatStore = useRoleChatStore()

// 角色信息
const role = ref(null)

// 角色消息正在输入中
const roleChatting = ref(false)

// 发送消息
const sendMessage = async (payload) => {
    // 校验发送的消息不能为空
    if (!message.value.trim()) {
        return;
    }

    // 关闭初次加载的动画
    isFirstLoad.value = false

    // 将用户发送的消息添加到 chatList 聊天列表中
    const userMessage = message.value.trim()
    chatList.value.unshift({ role: 'user', content: userMessage })

    // 滚动到底部   
    await nextTick(() => scrollToBottom())

    // 点击发送按钮后，清空输入框
    message.value = ''
    // 显示正在输入的动画
    roleChatting.value = true

    // 滚动到底部   
    await nextTick(() => scrollToBottom())

    try {
        // 构建请求体
        const requestBody = {
            message: userMessage,
            // TODO: 用户名需要登录获取
            username: '黑夜',
            chatId: chatId.value,
            // 这里角色名都是语音模型的角色名
            roleName: payload.selectedSoundModel?.soundModelName,
            textModelName: payload.selectedTextModel?.name,
            language: payload.selectedSoundModel?.language.name,
            networkSearch: payload.isNetworkSearch
        }

        // 响应的回答
        let responseText = ''

        const controller = new AbortController()
        const signal = controller.signal

        // 调用 SEE 流式对话接口
        await fetchEventSource('http://localhost:8080/role/chat/completion', {
            method: 'POST',
            signal: signal,
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(requestBody),
            onmessage(msg) {
                if (msg.event === '') {
                    // 收到第一条数据后设置 roleChatting 为 false, 并添加回复消息
                    if (roleChatting.value) {
                        // 添加一个占位的回复消息
                        chatList.value.unshift({ role: 'assistant', content: '', loading: true })

                        roleChatting.value = false
                    }

                    // 解析 JSON
                    let parseJson = JSON.parse(msg.data)
                    // 持续追加流式回答
                    responseText += parseJson.v

                    // 更新最后一条消息
                    chatList.value[0].content = responseText
                    // 滚动到底部
                    scrollToBottom()
                }
                else if (msg.event === 'close') {
                    console.log('-- sse close')
                    controller.abort();
                }
            },
            onerror(err) {
                // 必须 throw 才能停止 
                throw err;
            }
        })

    } catch (error) {
        console.error('发送消息错误: ', error)

        // 提示用户 “请求出错”
        chatList.value.unshift({ role: 'assistant', content: '抱歉，请求出错了，请稍后重试。'})
        roleChatting.value = false
        // 滚动到底部
        scrollToBottom()
    }

    // 将输入框的高度重置
    if (textareaRef.value) {
        textareaRef.value.style.height = 'auto'
    }
}

// 聊天容器引用
const chatContainer = ref(null)
// 聊天记录容器引用
const chatHistoryContainer = ref(null)

// 角色详情加载
const roleDetailLoading = ref(false)
// 聊天输入框加载动画
const chatInputBoxskeletonLoadingAnimation = ref(false)

// 滚动到底部
const scrollToBottom = async () => {
    console.info('scrollToBottom 滑动到了底部')
    // 等待 Vue.js 完成 DOM 更新
    await nextTick(() => {
        // 若容器存在
        if (chatHistoryContainer.value) {
            // 将容器的滚动条位置设置到最底部
            const container = chatHistoryContainer.value;
            container.scrollTop = container.scrollHeight
        }
    }) 
}

onMounted(() => {
    // 获取角色 ID
    const roleId = route.query?.roleId

    // 获取角色详情
    findRoleDetail(roleId)

    // 加载历史消息
    loadHistoryMessages()

    // 为聊天容器添加滚动事件监听器
    if (chatHistoryContainer.value) {
        // 添加监听事件
        chatHistoryContainer.value.addEventListener('scroll', handleScroll);
    }

    const firstMessage = history.state?.firstMessage
    // 检查跳转路由时，是否有初始消息
    if (firstMessage) {
        message.value = firstMessage
        // 发送消息
        sendMessage({
            selectedModel: chatStore.selectedModel,
            isNetworkSearch: chatStore.isNetworkSearchSelected
        })
    }

    // 发送消息后清除 history.state 中的 firstMessage，防止刷新页面时重复发送
    // 检查浏览器是否支持 History API
    if (history.replaceState) {
        // 创建当前状态的浅拷贝
        const newState = { ...history.state }
        // 移除特定的状态属性
        delete newState.firstMessage
        // 替换当前历史记录条目的状态而不创建新记录
        history.replaceState(newState, document.title)
    }
})

// 分页相关状态
// 当前页码（默认第一页）
const current = ref(1)
// 每页展示数据量
const size = ref(10)
// 是否还有下一页数据（默认有）
const hasMore = ref(true)
// 是否正在加载中 (解决并发请求后续页数据问题)
const isLoadingMore = ref(false)
// 初次查询历史聊天记录动画控制
const isFirstLoad = ref(true)
// 加载历史对话消息
const loadHistoryMessages = async () => {
    console.log('运行了 loadHistoryMessages')

    // 获取当前页码的聊天记录
    findChatMessagePageList(current.value, size.value, chatId.value).then((res) => {
        // 无论成功失败，请求完成后都需要重置加载状态
        isLoadingMore.value = false
        
        if (res.data.success) {
            const historyMessages = res.data.data
            // 判断是否还有下一页
            hasMore.value = res.data.pages > current.value

            if (historyMessages && historyMessages.length > 0) {
                // 重要：反转数据顺序，让最新的在底部
                const reversedMessages = [...historyMessages].reverse()

                // 将历史消息添加到聊天列表顶部
                chatList.value = [...chatList.value, ...reversedMessages]
                
                // 确保加载历史消息时自动滚动到底部 (仅第一页)
                if (current.value === 1) {
                    // 关闭初次加载的动画
                    isFirstLoad.value = false
                    // 滚动到底部
                    scrollToBottom()
                }
            }
        }
    }).catch((error) => { 
        // 错误处理，重置加载状态
        console.error('加载历史消息失败:', error)
        isLoadingMore.value = false
    })
}

// 组件卸载时自动关闭连接
onBeforeUnmount(() => {
  // 移除滚动事件监听
  if (chatHistoryContainer.value) {
    chatHistoryContainer.value.removeEventListener('scroll', handleScroll);
  }
})

// 监听滚动事件，添加了防抖，为了加载更多消息时可以更好的回到原位
const handleScroll = () => {
    if (chatHistoryContainer.value) {
        const container = chatHistoryContainer.value
        // 到滚动区域顶部的距离
        const scrollTop = Math.abs(container.scrollTop)

        // 打印滚动过程中的详细日志
        console.log('=== 滚动事件日志 ===')
        console.log('scrollTop:', scrollTop)
        console.log('isLoadingMore:', isLoadingMore.value)
        console.log('hasMore:', hasMore.value)
                
        // 当用户向上滚动到顶部附近时，且有更多数据，且当前没有在加载中时，才加载更多历史消息
        if (scrollTop <= 1000 && hasMore.value && !isLoadingMore.value) {
            console.log('=== 触发加载更多历史消息 ===');
            loadMoreHistoryMessages();
        }
    }
}
// 加载更多历史消息
const loadMoreHistoryMessages = async () => {
    console.log('运行了 loadMoreHistoryMessages')

    console.log('=== 开始加载更多历史消息 ===')
    console.log('当前页码:', current.value)
    
    // 双重检查：
    // 1. 如果当前页面已经是最后一页，则不再发送请求
    // 2. 如果已经有请求在进行中，则不再发送请求
    if (!hasMore.value) {
        console.log('=== 没有更多历史消息，不再请求 ===')
        return
    }

    if (isLoadingMore.value) {
        console.log('=== 已有加载请求正在进行中，不再发送新请求 ===')
        return
    }

    // 设置加载状态为 true，防止并发请求
    isLoadingMore.value = true

    // 计算下一页页码（向上滑动加载更早的历史消息，页码应该增加）
    const nextPageNo = current.value + 1
    console.log('=== 计算下一页页码 ===', nextPageNo)

    // 保存当前页面用于错误恢复
    const currentTemp = current.value
    // 当前需要请求的页码
    current.value = nextPageNo

    try {
        loadHistoryMessages();
    } catch (error) {
        // 恢复页码
        current.value = currentTemp;
    }
}

// 监听路由变化
watch(() => route.params.chatId, (newChatId) => {
    if (newChatId) {
        // 更新对话 ID
        chatId.value = newChatId
        // 清空历史消息
        chatList.value = []
        // 设置页码为第一页
        current.value = 1
        // 设置为正在查询更多状态
        isLoadingMore.value = true
        // 设置首次加载状态为 true
        isFirstLoad.value = true
        // 加载历史消息
        loadHistoryMessages()
    }
})

// 监听角色 ID 变化
watch(() => route.query.roleId, (newRoleId) => {
        // 获取角色详情
        findRoleDetail(newRoleId)
})

// 查询角色详情
const findRoleDetail = async (roleId) => { 
    
    // 开启加载角色详情的动画
    roleDetailLoading.value = true
    // 开启加载输入框 skeleton 动画
    chatInputBoxskeletonLoadingAnimation.value= true

    // 发送请求
    await findRoleById(roleId).then(res => {
            if (res.data.success) {
                
                // 角色详情信息
                const roleRes = res.data.data
                console.log('角色详情信息:', roleRes)


                // 获取角色支持文本模型
                const textModels = roleRes.textModels;
                const soundModels = roleRes.soundModels;
                console.log('角色文本模型:', roleRes.textModels)
                console.log('角色语音模型:', roleRes.soundModels)

                // 获取用户之前在该角选择的模型信息
                let selectedModel = roleChatStore.findSelectedRoleModelList(roleRes.roleId)
                console.log('查询到的角色模型信息:', selectedModel)

                // 如果没有选择过的模型，用户可能在之前没选择过角色对话，将该角色的模型信息初始化
                if (!selectedModel) {
                    // 获取 textModels 里的默认模型
                    const textModel = textModels.find(model => model.isDefault === true)
                    // 获取 soundModels 里的默认模型 soundModels 还包含了语言的信息还需要获取默认语言信息
                    let soundModel = null
                    let language = null
                    // soundModels 可能是空的, 需要判断
                    if (soundModels?.length > 0) {
                        soundModel = soundModels.find(model => model.isDefault === true)
                        language = soundModel.languages.find(language => language.isDefault === true)
                    }

                    // 排除掉里面的 languages 字段
                    const { languages, ...rest } = soundModel ? soundModel : {}

                    // 添加该角色对话过的模型
                    selectedModel = roleChatStore.addSelectedRoleModelList({
                        roleId: roleRes.roleId,
                        textModel: textModel,
                        // soundModel 为空时就设置为 null
                        soundModel: soundModel ? {
                            ...rest,
                            language,
                        } : null,
                    })
                }

                // 设置默认选中状态
                const processedTextModels = textModels.map(model => ({
                    ...model,
                    // 添加选中状态
                    selected: model.id === selectedModel.textModel.id
                }))

                // 处理的语音模型
                let processedSoundModels = null
                // 如果有语音模型，需要处理语音模型和语音模型中语言选择
                if (soundModels !== null && soundModels.length > 0) {

                    console.log('该角色的语音模型', selectedModel.soundModel)

                    // 这里还是要判断下存储在本地的该角色模型的语音模型是否存在
                    // 因为可能存在该角色本来不支持语音的，但是不断更新后该角色支持了语音
                    if (!selectedModel.soundModel) {
                        // 存储在本地的该角色模型的语音模型为空，但是后端的请求带来了角色有语音模型信息
                        // 修改 roleChatStore 中的该角色的语音模型信息
                        let soundModel = soundModels.find(model => model.isDefault === true)
                        const language = soundModel.languages.find(language => language.isDefault === true)

                        // 排除掉里面的 languages 字段
                        const { languages, ...rest } = soundModel

                        // 合并 rest 与 language 到 soundModel
                        soundModel = { ...rest, language }

                        // 修改该角色的语音模型
                        roleChatStore.updateSelectedRoleSoundModel(roleRes.roleId, soundModel)

                        // 重新获取该角色的模型信息
                        selectedModel = roleChatStore.findSelectedRoleModelList(roleRes.roleId)
                    }
                    
                    // 处理语音模型
                    processedSoundModels = soundModels.map(soundModel => {
                        // 处理语言
                        const processedLanguages = soundModel.languages.map(language => ({
                            ...language,
                            selected: language.id === selectedModel.soundModel.language.id
                        }))

                        // 处理语音模型本身
                        return {
                            ...soundModel,
                            languages: processedLanguages,
                            selected: soundModel.id === selectedModel.soundModel.id
                        }
                    })
                }

                // 赋值给 textModels 和 soundModels
                res.data.data.textModels = processedTextModels
                res.data.data.soundModels = processedSoundModels

                role.value = res.data.data

                console.log('处理后的角色详情信息:', role.value)
            }
        }
    )

    // 关闭加载角色详情的动画
    roleDetailLoading.value = false
    // 停止加载输入框 skeleton 动画
    chatInputBoxskeletonLoadingAnimation.value = false
}

// 生成角色语音
const generateRoleAudio = async (chat) => {
    // 获取用户选择的模型信息
    const selectedRoleModel = roleChatStore.findSelectedRoleModelList(role.value.roleId)
    // 准备请求数据
    const id = chat.id
    const roleName = selectedRoleModel.soundModel.soundModelName
    const modelName = selectedRoleModel.soundModel.modelName
    const soundModelVersion = selectedRoleModel.soundModel.version
    const language = selectedRoleModel.soundModel.language.name
    const text = chat.content

    // 发送获取角色语音的请求
    const audioUrl = await roleSoundGgeneration(id,roleName, modelName, soundModelVersion, language, text)
    .then(res => { 
        if (res.data.success) {
            // 返回角色语音音频地址
            const newUrl = res.data.data
            // 修改该条消息的音频地址
            chat.audioUrl = newUrl
            // 返回该条消息的音频地址
            return newUrl
        } else {
            console.log('获取角色语音失败:', res.data.message)
        }
    })

    return audioUrl
}
</script>

<style scoped>
.quesiton-container {
    font-size: 16px;
    line-height: 28px;
    color: #262626;
    padding: calc((44px - 28px) / 2) 20px;
    box-sizing: border-box;
    white-space: pre-wrap;
    word-break: break-word;
    background-color: #dfebff;
    border-radius: 14px;
    /* max-width: calc(100% - 48px); */
    max-width: 90%;
    position: relative;
}

.rotate-text {
    transform: rotate(180deg);           /* 主要变换：旋转180度 */
    -webkit-font-smoothing: antialiased; /* Chrome/Safari 字体平滑 */
    -moz-osx-font-smoothing: grayscale;  /* Firefox 字体平滑 */
    backface-visibility: hidden;         /* 隐藏背面，提升性能 */
    transform-style: preserve-3d;        /* 启用3D变换上下文 */
}
</style>
